# _*_ coding : utf-8 _*_
# @Time : 2025/5/4 15:40
# @Author : liuchengjie
# @File : DHT11
# @Project : esp32

import time
from machine import Pin


class DHT11:
    def __init__(self, pin):
        """
        初始化DHT11传感器

        参数:
        pin -- 数据引脚连接的GPIO编号
        """
        self.data_pin = Pin(pin, Pin.OUT)
        self.data_pin.value(1)  # 初始设置为高电平
        self.temperature = 0
        self.humidity = 0
        self.last_read = 0

    def _wait_us(self, us):
        """
        微秒级延时函数
        """
        start = time.ticks_us()
        while time.ticks_diff(time.ticks_us(), start) < us:
            pass

    def _start_signal(self):
        """
        发送开始信号给DHT11
        """
        # 配置为输出模式
        self.data_pin.init(Pin.OUT)

        # 拉低至少18ms以开始信号传输
        self.data_pin.value(0)
        time.sleep_ms(20)

        # 拉高20-40us
        self.data_pin.value(1)
        self._wait_us(30)

        # 切换为输入模式准备接收数据
        self.data_pin.init(Pin.IN, Pin.PULL_UP)

    def _read_response(self):
        """
        读取DHT11响应
        """
        # 等待DHT11拉低信号
        count = 0
        while self.data_pin.value():
            count += 1
            if count > 100:
                return False  # 超时
            self._wait_us(1)

        # 等待DHT11拉高信号
        count = 0
        while not self.data_pin.value():
            count += 1
            if count > 100:
                return False  # 超时
            self._wait_us(1)

        # 等待DHT11再次拉低信号
        count = 0
        while self.data_pin.value():
            count += 1
            if count > 100:
                return False  # 超时
            self._wait_us(1)

        return True

    def _read_data(self):
        """
        读取40位数据 (8位湿度整数 + 8位湿度小数 + 8位温度整数 + 8位温度小数 + 8位校验和)
        """
        data = []
        for _ in range(5):  # 读取5个字节
            byte = 0
            for bit in range(8):  # 每个字节8位
                # 每一位数据前都有一个50us的低电平
                count = 0
                while not self.data_pin.value():
                    count += 1
                    if count > 100:
                        return None  # 超时
                    self._wait_us(1)

                # 测量高电平持续时间
                count = 0
                start = time.ticks_us()
                while self.data_pin.value():
                    count += 1
                    if count > 100:
                        return None  # 超时
                    self._wait_us(1)
                duration = time.ticks_diff(time.ticks_us(), start)

                # 高电平持续时间 >40us 表示1，否则为0
                if duration > 40:
                    byte |= (1 << (7 - bit))

            data.append(byte)

        return data

    def read(self):
        """
        读取温湿度数据

        返回:
        tuple -- (是否成功读取, 湿度值, 温度值)
        """
        # 限制读取频率，DHT11通常每秒只能读取一次
        current_time = time.time()
        if current_time - self.last_read < 2:
            return (True, self.humidity, self.temperature)

        self.last_read = current_time

        # 发送开始信号
        self._start_signal()

        # 读取DHT11响应
        if not self._read_response():
            return (False, 0, 0)

        # 读取数据
        data = self._read_data()
        if data is None:
            return (False, 0, 0)

        # 验证校验和
        checksum = (data[0] + data[1] + data[2] + data[3]) & 0xFF
        if checksum != data[4]:
            return (False, 0, 0)

        # DHT11中湿度小数部分(data[1])和温度小数部分(data[3])通常为0
        self.humidity = data[0]
        self.temperature = data[2]

        return (True, self.humidity, self.temperature)
